{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "04791372",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(0, 1)\n",
      "(0, 12)\n",
      "(1, 1)\n",
      "(1, 12)\n",
      "(3, 5)\n",
      "(3, 8)\n",
      "(4, 3)\n",
      "(4, 10)\n",
      "(5, 2)\n",
      "(5, 11)\n",
      "(6, 4)\n",
      "(6, 9)\n",
      "(7, 5)\n",
      "(7, 8)\n",
      "(10, 4)\n",
      "(10, 9)\n",
      "(12, 1)\n",
      "(12, 12)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "# Define the parameters of the elliptic curve over the finite field F_13\n",
    "p = 13\n",
    "a = -1\n",
    "b = 1\n",
    "\n",
    "# Define all the points over the finite field F_13\n",
    "x = np.arange(p)\n",
    "y = np.arange(p)\n",
    "\n",
    "# Enumerate the points on the elliptic curve\n",
    "curve_points = []\n",
    "for xi in x:\n",
    "    for yi in y:\n",
    "        if (yi**2) % p == (xi**3 + a*xi + b) % p:\n",
    "            print((xi, yi))\n",
    "            curve_points.append((xi, yi))\n",
    "\n",
    "# Unpack the coordinates of the points\n",
    "curve_points_x, curve_points_y = zip(*curve_points)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a3a9123c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 绘制椭圆曲线\n",
    "plt.figure(figsize=(6, 6))\n",
    "plt.scatter(curve_points_x, curve_points_y, color='blue')\n",
    "plt.axhline(p/2, color='red',linewidth=0.5)\n",
    "plt.title('Elliptic Curve over Finite Field F_13')\n",
    "plt.xlabel('x')\n",
    "plt.ylabel('y')\n",
    "plt.grid(True)\n",
    "plt.xticks(np.arange(p))\n",
    "plt.yticks(np.arange(p))\n",
    "plt.gca().set_aspect('equal', adjustable='box')\n",
    "\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "4cd0319a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                (0, 1)     (0, 12)      (1, 1)     (1, 12)      (3, 5)  \\\n",
      "(0, 1)         (10, 4)  (inf, inf)    (12, 12)      (3, 5)      (6, 4)   \n",
      "(0, 12)     (inf, inf)     (10, 9)      (3, 8)     (12, 1)     (1, 12)   \n",
      "(1, 1)        (12, 12)      (3, 8)     (12, 1)  (inf, inf)      (0, 1)   \n",
      "(1, 12)         (3, 5)     (12, 1)  (inf, inf)    (12, 12)      (5, 2)   \n",
      "(3, 5)          (6, 4)     (1, 12)      (0, 1)      (5, 2)      (7, 8)   \n",
      "(3, 8)          (1, 1)      (6, 9)     (5, 11)     (0, 12)  (inf, inf)   \n",
      "(4, 3)          (6, 9)     (10, 4)      (7, 8)     (4, 10)     (10, 9)   \n",
      "(4, 10)        (10, 9)      (6, 4)      (4, 3)      (7, 5)     (5, 11)   \n",
      "(5, 2)          (7, 8)    (12, 12)      (3, 5)     (10, 4)      (4, 3)   \n",
      "(5, 11)        (12, 1)      (7, 5)     (10, 9)      (3, 8)      (1, 1)   \n",
      "(6, 4)         (4, 10)      (3, 5)     (10, 4)      (7, 8)      (7, 5)   \n",
      "(6, 9)          (3, 8)      (4, 3)      (7, 5)     (10, 9)     (0, 12)   \n",
      "(7, 5)         (5, 11)      (7, 8)     (4, 10)      (6, 9)      (3, 8)   \n",
      "(7, 8)          (7, 5)      (5, 2)      (6, 4)      (4, 3)      (6, 9)   \n",
      "(10, 4)         (4, 3)      (0, 1)      (5, 2)      (6, 4)     (4, 10)   \n",
      "(10, 9)        (0, 12)     (4, 10)      (6, 9)     (5, 11)     (12, 1)   \n",
      "(12, 1)        (1, 12)     (5, 11)     (0, 12)      (1, 1)    (12, 12)   \n",
      "(12, 12)        (5, 2)      (1, 1)     (1, 12)      (0, 1)     (10, 4)   \n",
      "(inf, inf)      (0, 1)     (0, 12)      (1, 1)     (1, 12)      (3, 5)   \n",
      "\n",
      "                (3, 8)      (4, 3)     (4, 10)      (5, 2)     (5, 11)  \\\n",
      "(0, 1)          (1, 1)      (6, 9)     (10, 9)      (7, 8)     (12, 1)   \n",
      "(0, 12)         (6, 9)     (10, 4)      (6, 4)    (12, 12)      (7, 5)   \n",
      "(1, 1)         (5, 11)      (7, 8)      (4, 3)      (3, 5)     (10, 9)   \n",
      "(1, 12)        (0, 12)     (4, 10)      (7, 5)     (10, 4)      (3, 8)   \n",
      "(3, 5)      (inf, inf)     (10, 9)     (5, 11)      (4, 3)      (1, 1)   \n",
      "(3, 8)          (7, 5)      (5, 2)     (10, 4)     (1, 12)     (4, 10)   \n",
      "(4, 3)          (5, 2)      (1, 1)  (inf, inf)     (5, 11)      (3, 5)   \n",
      "(4, 10)        (10, 4)  (inf, inf)     (1, 12)      (3, 8)      (5, 2)   \n",
      "(5, 2)         (1, 12)     (5, 11)      (3, 8)     (4, 10)  (inf, inf)   \n",
      "(5, 11)        (4, 10)      (3, 5)      (5, 2)  (inf, inf)      (4, 3)   \n",
      "(6, 4)          (0, 1)     (0, 12)     (12, 1)      (6, 9)    (12, 12)   \n",
      "(6, 9)          (7, 8)    (12, 12)      (0, 1)     (12, 1)      (6, 4)   \n",
      "(7, 5)          (6, 4)     (1, 12)    (12, 12)     (0, 12)     (10, 4)   \n",
      "(7, 8)          (3, 5)     (12, 1)      (1, 1)     (10, 9)      (0, 1)   \n",
      "(10, 4)       (12, 12)      (3, 8)     (0, 12)      (7, 5)     (1, 12)   \n",
      "(10, 9)         (4, 3)      (0, 1)      (3, 5)      (1, 1)      (7, 8)   \n",
      "(12, 1)        (10, 9)      (6, 4)      (7, 8)      (0, 1)      (6, 9)   \n",
      "(12, 12)       (12, 1)      (7, 5)      (6, 9)      (6, 4)     (0, 12)   \n",
      "(inf, inf)      (3, 8)      (4, 3)     (4, 10)      (5, 2)     (5, 11)   \n",
      "\n",
      "                (6, 4)      (6, 9)      (7, 5)      (7, 8)     (10, 4)  \\\n",
      "(0, 1)         (4, 10)      (3, 8)     (5, 11)      (7, 5)      (4, 3)   \n",
      "(0, 12)         (3, 5)      (4, 3)      (7, 8)      (5, 2)      (0, 1)   \n",
      "(1, 1)         (10, 4)      (7, 5)     (4, 10)      (6, 4)      (5, 2)   \n",
      "(1, 12)         (7, 8)     (10, 9)      (6, 9)      (4, 3)      (6, 4)   \n",
      "(3, 5)          (7, 5)     (0, 12)      (3, 8)      (6, 9)     (4, 10)   \n",
      "(3, 8)          (0, 1)      (7, 8)      (6, 4)      (3, 5)    (12, 12)   \n",
      "(4, 3)         (0, 12)    (12, 12)     (1, 12)     (12, 1)      (3, 8)   \n",
      "(4, 10)        (12, 1)      (0, 1)    (12, 12)      (1, 1)     (0, 12)   \n",
      "(5, 2)          (6, 9)     (12, 1)     (0, 12)     (10, 9)      (7, 5)   \n",
      "(5, 11)       (12, 12)      (6, 4)     (10, 4)      (0, 1)     (1, 12)   \n",
      "(6, 4)         (5, 11)  (inf, inf)      (1, 1)      (3, 8)     (10, 9)   \n",
      "(6, 9)      (inf, inf)      (5, 2)      (3, 5)     (1, 12)      (1, 1)   \n",
      "(7, 5)          (1, 1)      (3, 5)      (0, 1)  (inf, inf)     (12, 1)   \n",
      "(7, 8)          (3, 8)     (1, 12)  (inf, inf)     (0, 12)     (5, 11)   \n",
      "(10, 4)        (10, 9)      (1, 1)     (12, 1)     (5, 11)      (6, 9)   \n",
      "(10, 9)        (1, 12)     (10, 4)      (5, 2)    (12, 12)  (inf, inf)   \n",
      "(12, 1)         (5, 2)     (4, 10)      (4, 3)     (10, 4)      (3, 5)   \n",
      "(12, 12)        (4, 3)     (5, 11)     (10, 9)     (4, 10)      (7, 8)   \n",
      "(inf, inf)      (6, 4)      (6, 9)      (7, 5)      (7, 8)     (10, 4)   \n",
      "\n",
      "               (10, 9)     (12, 1)    (12, 12)  (inf, inf)  \n",
      "(0, 1)         (0, 12)     (1, 12)      (5, 2)      (0, 1)  \n",
      "(0, 12)        (4, 10)     (5, 11)      (1, 1)     (0, 12)  \n",
      "(1, 1)          (6, 9)     (0, 12)     (1, 12)      (1, 1)  \n",
      "(1, 12)        (5, 11)      (1, 1)      (0, 1)     (1, 12)  \n",
      "(3, 5)         (12, 1)    (12, 12)     (10, 4)      (3, 5)  \n",
      "(3, 8)          (4, 3)     (10, 9)     (12, 1)      (3, 8)  \n",
      "(4, 3)          (0, 1)      (6, 4)      (7, 5)      (4, 3)  \n",
      "(4, 10)         (3, 5)      (7, 8)      (6, 9)     (4, 10)  \n",
      "(5, 2)          (1, 1)      (0, 1)      (6, 4)      (5, 2)  \n",
      "(5, 11)         (7, 8)      (6, 9)     (0, 12)     (5, 11)  \n",
      "(6, 4)         (1, 12)      (5, 2)      (4, 3)      (6, 4)  \n",
      "(6, 9)         (10, 4)     (4, 10)     (5, 11)      (6, 9)  \n",
      "(7, 5)          (5, 2)      (4, 3)     (10, 9)      (7, 5)  \n",
      "(7, 8)        (12, 12)     (10, 4)     (4, 10)      (7, 8)  \n",
      "(10, 4)     (inf, inf)      (3, 5)      (7, 8)     (10, 4)  \n",
      "(10, 9)         (6, 4)      (7, 5)      (3, 8)     (10, 9)  \n",
      "(12, 1)         (7, 5)      (3, 8)  (inf, inf)     (12, 1)  \n",
      "(12, 12)        (3, 8)  (inf, inf)      (3, 5)    (12, 12)  \n",
      "(inf, inf)     (10, 9)     (12, 1)    (12, 12)  (inf, inf)  \n"
     ]
    }
   ],
   "source": [
    "from sympy import mod_inverse\n",
    "\n",
    "# 定义有限域F_13的椭圆曲线参数\n",
    "p = 13\n",
    "a = -1\n",
    "b = 1\n",
    "\n",
    "# 计算椭圆曲线上的点\n",
    "curve_points = []\n",
    "for xi in range(p):\n",
    "    for yi in range(p):\n",
    "        if (yi**2) % p == (xi**3 + a*xi + b) % p:\n",
    "            curve_points.append((xi, yi))\n",
    "\n",
    "# 添加无穷远点\n",
    "curve_points.append(('inf', 'inf'))\n",
    "\n",
    "# 定义加法运算\n",
    "def elliptic_curve_addition(P, Q, a, p):\n",
    "    if P == ('inf', 'inf'):\n",
    "        return Q\n",
    "    if Q == ('inf', 'inf'):\n",
    "        return P\n",
    "    if P[0] == Q[0] and (P[1] != Q[1] or P[1] == 0):\n",
    "        # P + Q = O (无穷远点) 如果它们是垂直对称的点或P是切点\n",
    "        return ('inf', 'inf')\n",
    "    if P == Q:\n",
    "        # 点翻倍\n",
    "        lambda_ = (3 * P[0]**2 + a) * mod_inverse(2 * P[1], p) % p\n",
    "    else:\n",
    "        # 点加法\n",
    "        lambda_ = (Q[1] - P[1]) * mod_inverse(Q[0] - P[0], p) % p\n",
    "\n",
    "    x3 = (lambda_**2 - P[0] - Q[0]) % p\n",
    "    y3 = (lambda_ * (P[0] - x3) - P[1]) % p\n",
    "\n",
    "    return (x3, y3)\n",
    "\n",
    "# 创建加法表格\n",
    "addition_table = [[None for _ in curve_points] for _ in curve_points]\n",
    "for i, P in enumerate(curve_points):\n",
    "    for j, Q in enumerate(curve_points):\n",
    "        addition_table[i][j] = elliptic_curve_addition(P, Q, a, p)\n",
    "\n",
    "# 打印加法表格\n",
    "# for row in addition_table:\n",
    "#     print(row)\n",
    "\n",
    "import pandas as pd\n",
    "\n",
    "# 将加法表格转换为Pandas DataFrame以美化输出\n",
    "addition_table_df = pd.DataFrame(addition_table, index=curve_points, columns=curve_points)\n",
    "\n",
    "# 设置显示选项以确保表格能够整齐地打印\n",
    "pd.set_option('display.max_rows', None)\n",
    "pd.set_option('display.max_columns', None)\n",
    "pd.set_option('display.width', None)\n",
    "pd.set_option('display.max_colwidth', None)\n",
    "\n",
    "# 打印加法表格\n",
    "print(addition_table_df)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0538a937",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(0, 0)\n",
      "(1, 0)\n",
      "(2, 1)\n",
      "(2, 4)\n",
      "(3, 2)\n",
      "(3, 3)\n",
      "(4, 0)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "# 定义有限域F_13的椭圆曲线参数\n",
    "p = 5\n",
    "a = -1\n",
    "b = 0\n",
    "\n",
    "# 定义有限域F_13下的所有点\n",
    "x = np.arange(p)\n",
    "y = np.arange(p)\n",
    "\n",
    "# 计算椭圆曲线上的点\n",
    "curve_points = []\n",
    "for xi in x:\n",
    "    for yi in y:\n",
    "        if (yi**2) % p == (xi**3 + a*xi + b) % p:\n",
    "            print((xi, yi))\n",
    "            curve_points.append((xi, yi))\n",
    "\n",
    "# 解压点坐标\n",
    "curve_points_x, curve_points_y = zip(*curve_points)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "48665179",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                (0, 0)      (1, 0)      (2, 1)      (2, 4)      (3, 2)  \\\n",
      "(0, 0)      (inf, inf)      (4, 0)      (2, 4)      (2, 1)      (3, 3)   \n",
      "(1, 0)          (4, 0)  (inf, inf)      (3, 3)      (3, 2)      (2, 4)   \n",
      "(2, 1)          (2, 4)      (3, 3)      (0, 0)  (inf, inf)      (1, 0)   \n",
      "(2, 4)          (2, 1)      (3, 2)  (inf, inf)      (0, 0)      (4, 0)   \n",
      "(3, 2)          (3, 3)      (2, 4)      (1, 0)      (4, 0)      (0, 0)   \n",
      "(3, 3)          (3, 2)      (2, 1)      (4, 0)      (1, 0)  (inf, inf)   \n",
      "(4, 0)          (1, 0)      (0, 0)      (3, 2)      (3, 3)      (2, 1)   \n",
      "(inf, inf)      (0, 0)      (1, 0)      (2, 1)      (2, 4)      (3, 2)   \n",
      "\n",
      "                (3, 3)      (4, 0)  (inf, inf)  \n",
      "(0, 0)          (3, 2)      (1, 0)      (0, 0)  \n",
      "(1, 0)          (2, 1)      (0, 0)      (1, 0)  \n",
      "(2, 1)          (4, 0)      (3, 2)      (2, 1)  \n",
      "(2, 4)          (1, 0)      (3, 3)      (2, 4)  \n",
      "(3, 2)      (inf, inf)      (2, 1)      (3, 2)  \n",
      "(3, 3)          (0, 0)      (2, 4)      (3, 3)  \n",
      "(4, 0)          (2, 4)  (inf, inf)      (4, 0)  \n",
      "(inf, inf)      (3, 3)      (4, 0)  (inf, inf)  \n"
     ]
    }
   ],
   "source": [
    "from sympy import mod_inverse\n",
    "\n",
    "# 定义有限域F_13的椭圆曲线参数\n",
    "p = 5\n",
    "a = -1\n",
    "b = 0\n",
    "\n",
    "# 计算椭圆曲线上的点\n",
    "curve_points = []\n",
    "for xi in range(p):\n",
    "    for yi in range(p):\n",
    "        if (yi**2) % p == (xi**3 + a*xi + b) % p:\n",
    "            curve_points.append((xi, yi))\n",
    "\n",
    "# 添加无穷远点\n",
    "curve_points.append(('inf', 'inf'))\n",
    "\n",
    "# 定义加法运算\n",
    "def elliptic_curve_addition(P, Q, a, p):\n",
    "    if P == ('inf', 'inf'):\n",
    "        return Q\n",
    "    if Q == ('inf', 'inf'):\n",
    "        return P\n",
    "    if P[0] == Q[0] and (P[1] != Q[1] or P[1] == 0):\n",
    "        # P + Q = O (无穷远点) 如果它们是垂直对称的点或P是切点\n",
    "        return ('inf', 'inf')\n",
    "    if P == Q:\n",
    "        # 点翻倍\n",
    "        lambda_ = (3 * P[0]**2 + a) * mod_inverse(2 * P[1], p) % p\n",
    "    else:\n",
    "        # 点加法\n",
    "        lambda_ = (Q[1] - P[1]) * mod_inverse(Q[0] - P[0], p) % p\n",
    "\n",
    "    x3 = (lambda_**2 - P[0] - Q[0]) % p\n",
    "    y3 = (lambda_ * (P[0] - x3) - P[1]) % p\n",
    "\n",
    "    return (x3, y3)\n",
    "\n",
    "# 创建加法表格\n",
    "addition_table = [[None for _ in curve_points] for _ in curve_points]\n",
    "for i, P in enumerate(curve_points):\n",
    "    for j, Q in enumerate(curve_points):\n",
    "        addition_table[i][j] = elliptic_curve_addition(P, Q, a, p)\n",
    "\n",
    "# 打印加法表格\n",
    "# for row in addition_table:\n",
    "#     print(row)\n",
    "\n",
    "import pandas as pd\n",
    "\n",
    "# 将加法表格转换为Pandas DataFrame以美化输出\n",
    "addition_table_df = pd.DataFrame(addition_table, index=curve_points, columns=curve_points)\n",
    "\n",
    "# 设置显示选项以确保表格能够整齐地打印\n",
    "pd.set_option('display.max_rows', None)\n",
    "pd.set_option('display.max_columns', None)\n",
    "pd.set_option('display.width', None)\n",
    "pd.set_option('display.max_colwidth', None)\n",
    "\n",
    "# 打印加法表格\n",
    "print(addition_table_df)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2aa3b533",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
